/*
 * Copyright 2011
 * This file is part of XXXXXX.
 * XXXXXX is free software; you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 * 
 * XXXXXX is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * XXXXXX; if not, write to the Free Software Foundation, Inc., 51 Franklin St,
 * Fifth Floor, Boston, MA 02110-1301 USA
 */
package PD.modules.simulation;

import PD.data.NetworkDataset;
import PD.data.Row;
import PD.data.impl.datasets.SimpleNetworkDataset;
import PD.modules.simulation.Body.Body;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author bicha
 */
public class Bug {

        private NetworkDataset initialSubNetwork, finalNetwork, network;
        private Body bugBody;
        private int maxNumberOfChromosomes = 10;
        private double weight;
        private int stoppingCriteria;

        public Bug(NetworkDataset network, NetworkDataset subNetwork, int maxNumberOfChromosomes, int stoppingCriteria) {
                this.network = network;
                this.initialSubNetwork = subNetwork;
                this.maxNumberOfChromosomes = maxNumberOfChromosomes;
                this.stoppingCriteria = stoppingCriteria;
                this.getSubNetwork();
        }

        Bug(Bug mother, Bug father, int size, Row motherSeed, int maxNumberOfChromosomes, int stoppingCriteria) {
                List<Row> Nodes = new ArrayList<Row>();
                List<Row> Edges = new ArrayList<Row>();
                if (mother.finalNetwork != null && father.finalNetwork != null) {
                        for (Row node : mother.finalNetwork.getNodesRows()) {
                                Nodes.add(node.clone());
                        }
                        for (Row edge : mother.finalNetwork.getEdgesRows()) {
                                Edges.add(edge.clone());
                        }


                        constructNetwork(Nodes, Edges, size, motherSeed, father.finalNetwork);
                        SimpleNetworkDataset subNetwork = new SimpleNetworkDataset("subNet");
                        subNetwork.setEdgesRows(Edges);
                        subNetwork.setNodesRows(Nodes);
                        this.initialSubNetwork = subNetwork;
                        this.stoppingCriteria = stoppingCriteria;
                        this.maxNumberOfChromosomes = maxNumberOfChromosomes;
                      
                        this.getSubNetwork();                      
                }
        }

        private void constructNetwork(List<Row> Nodes, List<Row> Edges, int size, Row seed, NetworkDataset network) {
                List<Row> edges = network.getEdgesfromSourceID(seed.getID());
                for (Row edge : edges) {
                        Row targetNode = network.getNodesRowByID(edge.getTargetVertexID());
                        if (targetNode != null && ! contain(Nodes, targetNode)) {
                                Nodes.add(targetNode.clone());
                                Edges.add(edge.clone());
                                constructNetwork(Nodes, Edges, size, targetNode, network);
                        }
                }

        }

        private boolean contain(List<Row> list, Row row){
                for(Row rows: list){
                        if(rows.getID() == row.getID()){
                                return true;
                        }
                }
                return false;
        }

        private void getSubNetwork() {
                bugBody = new Body(initialSubNetwork, this.maxNumberOfChromosomes);
                int counter = 0;
                double maxWeight = 0;
                while (1 == 1) {
                        bugBody.cicle();
                        double localWeight = bugBody.getWeight();
                        if ((localWeight > maxWeight)) {
                                maxWeight = localWeight;
                                counter = 0;
                        }

                        counter++;

                        if (counter == stoppingCriteria) {
                                finalNetwork = bugBody.getBestChromosome().getDataset();
                                break;
                        }
                }
                this.completeNetwork();
        }

        public Row isConected(Bug father) {
                try {
                        for (Row node : this.finalNetwork.getNodesRows()) {
                                for (Row fnode : father.finalNetwork.getNodesRows()) {
                                        if (node.getID() == fnode.getID()) {
                                                return node;
                                        }
                                }
                        }
                        return null;
                } catch (NullPointerException e) {
                        return null;
                }
        }

        public double getWeight() {
                return weight;
        }

        public NetworkDataset getFinalDataset() {
                return this.finalNetwork;
        }

        public void completeNetwork() {
                if (finalNetwork != null) {
                        List<Row> newNodes = new ArrayList<Row>();
                        List<Row> newEdges = new ArrayList<Row>();
                        List<Row> nodes = finalNetwork.getNodesRows();
                        this.weight = calculateWeight();
                        for (Row node : nodes) {
                                try {
                                        List<Row> candidateNodes = network.getConnectedNodes(node.getID());
                                        if (candidateNodes != null) {
                                                for (int i = 0; i < candidateNodes.size(); i = i + 2) {
                                                        Row newEdge = candidateNodes.get(i);
                                                        Row newNode = candidateNodes.get(i + 1);
                                                        double newWeight = this.calculateWeight(nodes, finalNetwork.getEdgesRows(), newNode, newEdge);
                                                        if (newWeight >= this.weight) {
                                                                newEdges.add(newEdge.clone());
                                                                newNodes.add(newNode.clone());
                                                        }
                                                }
                                        }
                                } catch (NullPointerException e) {
                                }
                        }
                        for (int i = 0; i < newNodes.size(); i++) {
                                if (!this.finalNetwork.contains(newNodes.get(i).getID())) {
                                        this.finalNetwork.setNodesRow(newNodes.get(i));
                                        this.finalNetwork.setEdgesRow(newEdges.get(i));
                                }
                        }
                        this.weight = calculateWeight();
                }
        }

        private double calculateWeight(List<Row> nodes, List<Row> edges, Row newNode, Row newEdge) {
                double sum = 0, product = 1;
                for (Row node : nodes) {
                        sum += node.getWeight();
                }
                sum += newNode.getWeight();
                for (Row edge : edges) {
                        product *= edge.getWeight();
                }

                product *= newEdge.getWeight();

                return Math.abs(sum * product);
        }

        private double calculateWeight() {
                double sum = 0, product = 1;
                for (Row node : this.finalNetwork.getNodesRows()) {
                        sum += node.getWeight();
                }
                for (Row edge : this.finalNetwork.getEdgesRows()) {
                        product *= edge.getWeight();
                }

                return Math.abs(sum * product);
        }

        public boolean isEqual(Bug bug) {
                if (this.finalNetwork != null) {
                        for (Row row : this.finalNetwork.getNodesRows()) {
                                boolean isFound = false;
                                if (bug.getFinalDataset() != null) {
                                        for (Row row2 : bug.getFinalDataset().getNodesRows()) {
                                                if (row.getID() == row2.getID()) {
                                                        isFound = true;
                                                }
                                        }
                                        if (!isFound) {
                                                return false;
                                        }
                                } else {
                                        return false;
                                }
                        }
                        return true;
                } else {
                        return false;
                }
        }
}
